#ifndef _REFLECT_H_
#define _REFLECT_H_

#include <string>
#include <tuple>
#include <unordered_map>

template<typename T>
struct FuncInfo
{
    using Type = T;
    std::string name;
    T ptr;

    template<typename U, typename R, typename... Args>
    R Call(U & obj, Args&&... args) {
        return (obj.*ptr)(std::forward<Args>(args)...);
    }
};    

template<typename T, typename V>
struct FieldInfo
{
    using Type = T;
    size_t offset;
    std::string name;
    V fieldPtr;
    std::unordered_map<int, std::string> enums;
    
    template<typename U>
    T & Value(U & object) {
        return *(T*)((size_t)&object + offset);
    }

    template<typename U, typename V>
    void Value(U & object, V value) {
        memcpy((void*)((size_t)&object + offset), (void*)&value, sizeof(T));
    }

    constexpr bool IsClass() {
        return std::is_class_v<Type>;
    }

    constexpr bool IsEnum() {
        return std::is_enum_v<Type>;
    }

    constexpr const std::unordered_map<int, std::string> & Enums() const {
        return enums;
    }

    template<typename U>
    constexpr bool Is() {
        return std::is_same_v<Type, std::decay_t<U>>;
    }

};

template<typename T>
struct is_funcInfo { constexpr static bool value = false; };

template<typename T>
struct is_funcInfo<FuncInfo<T>> { constexpr static bool value = true; };

template<typename T>
constexpr bool is_funcInfo_v = is_funcInfo<T>::value;

template<typename T>
struct ReflectCRTP
{
    template<typename F, size_t idx = 0>
    constexpr void Foreach(F && fn) {
        if constexpr (idx < std::tuple_size_v<decltype(static_cast<T*>(this)->m_fields)>) {
            fn(std::get<idx>(static_cast<T*>(this)->m_fields));
            Foreach<F, idx + 1>(std::forward<F>(fn));
        }
    }

    template<typename F, size_t idx = 0>
    constexpr void ForeachField(F && fn) {
        if constexpr (idx < std::tuple_size_v<decltype(static_cast<T*>(this)->m_fields)>) {
            auto info = std::get<idx>(static_cast<T*>(this)->m_fields); 
            if constexpr (!is_funcInfo_v<decltype(info)>) {
                fn(info);
            }
            ForeachField<F, idx + 1>(std::forward<F>(fn));
        }
    }

    template<typename F, size_t idx = 0>
    constexpr void ForeachFunc(F && fn) {
        if constexpr (idx < std::tuple_size_v<decltype(static_cast<T*>(this)->m_fields)>) {
            auto info = std::get<idx>(static_cast<T*>(this)->m_fields); 
            if constexpr (is_funcInfo_v<decltype(info)>) {
                fn(info);
            }
            ForeachFunc<F, idx + 1>(std::forward<F>(fn));
        }
    }

    constexpr std::string Name() {
        return static_cast<T*>(this)->m_name;
    }   

    constexpr int FieldSize() {
        return std::tuple_size_v<decltype(static_cast<T*>(this)->m_fields)>;
    }

    template<size_t idx>
    constexpr auto Get() {
        return std::get<idx>(static_cast<T*>(this)->m_fields);
    }

    template<typename U>
    constexpr auto Get() {
        return std::get<FieldInfo<U>>(static_cast<T*>(this)->m_fields);
    }

    template<typename F>
    constexpr void Visit(F && fn, const std::string & fieldName) {
        this->Foreach([&fieldName, fn = std::forward<F>(fn)](auto field) {
            if (field.name == fieldName) {
                fn(field);
            }
        });
    }
};

template<typename T>
class Reflect : public ReflectCRTP<Reflect<T>> 
{
    template<typename U> 
    friend struct ReflectCRTP;
public:
    constexpr static bool Reflectable() { return is_refEnabled; }
private:
    constexpr inline static bool is_refEnabled = false; 
};

#define REF_ENABLE \
    template<typename T> \
    friend class Reflect; \

#define REF_ENUM(enumClass, enum) {#enum, (int)enumClass::enum}

#define REF_REGISTER_FIELD(className, fieldName) \
    FieldInfo<decltype(className::fieldName), decltype(&className::fieldName)>{offsetof(className, fieldName), #fieldName, &className::fieldName}

#define REF_REGISTER_ENUM(className, fieldName, ...) \
    FieldInfo<decltype(className::fieldName), decltype(&className::fieldName)>{offsetof(className, fieldName), #fieldName, &className::fieldName, {__VA_ARGS__}}

#define REF_REGISTER_FUNC(className, funcName) \
    FuncInfo<decltype(&className::funcName)>{#funcName, &className::funcName}

#define REF_REGISTER_CLASS(className, ...) \
    template<> \
    class Reflect<className> : public ReflectCRTP<Reflect<className>> { \
    public: \
        constexpr static bool Reflectable() { return is_refEnabled; } \
    private: \
        template<typename T> \
        friend struct ReflectCRTP; \
        constexpr inline static bool is_refEnabled = true; \
        inline static std::string m_name = #className; \
        inline static auto m_fields = std::make_tuple(__VA_ARGS__); \
    };

#define REF_IS_REFLECTABLE(field) Reflect<std::decay_t<decltype(field)>::Type>().Reflectable()

#endif